home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / tde3.zip / FILE.C < prev    next >
C/C++ Source or Header  |  1993-06-05  |  68KB  |  2,043 lines

  1. /*
  2.  * This file contains the file i/o stuff.  These functions get stuff from
  3.  * from the outside world into a form for TDE.  The form TDE uses is a
  4.  * double linked list.  Each node in the list points to the prev and
  5.  * the next nodes, if they exist.  Also in each node is a pointer to a
  6.  * line of text, a line length variable, and a dirty node indicator.  In
  7.  * earlier versions of TDE, a '\n' was used to terminate a line of text.
  8.  * In this version, we must keep an accurate count of characters in
  9.  * each line of text, as no character is used to terminate the line.
  10.  *
  11.  * Each file must contain at least one node.  That node is called the
  12.  * EOF node.  The EOF node terminates the double linked list for each
  13.  * file.  The EOF node has a NULL pointer in the line field, a NULL
  14.  * pointer in the next field, and an EOF in the len field.  Here's
  15.  * a crude picture of the double linked list structure:
  16.  *
  17.  *              Regular node                             EOF node
  18.  *     ---------                                 ---------
  19.  *     | prev  | ---> pointer to prev node       | prev  | ---> unknown
  20.  *     | line  | ---> "Hello world"              | line  | ---> NULL
  21.  *     | len   | ---> 11                         | len   | ---> EOF
  22.  *     | dirty | ---> TRUE | FALSE               | dirty | ---> FALSE
  23.  *     | next  | ---> pointer to next node       | next  | ---> NULL
  24.  *     ---------                                 ---------
  25.  *
  26.  * Implicitly, I am assuming that EOF is defined as (-1) in stdio.h.
  27.  *
  28.  * The load_file function is probably more complicated than expected, but
  29.  * I was trying to read chunks of text that match the disk cluster size
  30.  * and/or some multiple of the cache in the disk controller.
  31.  *
  32.  *
  33.  * New editor name:  TDE, the Thomson-Davis Editor.
  34.  * Author:           Frank Davis
  35.  * Date:             June 5, 1991, version 1.0
  36.  * Date:             July 29, 1991, version 1.1
  37.  * Date:             October 5, 1991, version 1.2
  38.  * Date:             January 20, 1992, version 1.3
  39.  * Date:             February 17, 1992, version 1.4
  40.  * Date:             April 1, 1992, version 1.5
  41.  * Date:             June 5, 1992, version 2.0
  42.  * Date:             October 31, 1992, version 2.1
  43.  * Date:             April 1, 1993, version 2.2
  44.  * Date:             June 5, 1993, version 3.0
  45.  *
  46.  * This code is released into the public domain, Frank Davis.
  47.  * You may distribute it freely.
  48.  */
  49.  
  50.  
  51. #include "tdestr.h"             /* tde types */
  52. #include "common.h"
  53. #include "define.h"
  54. #include "tdefunc.h"
  55.  
  56.  
  57. #include <dos.h>                /* for renaming files */
  58. #include <bios.h>               /* for direct BIOS keyboard input */
  59. #include <io.h>                 /* for file attribute code */
  60. #include <fcntl.h>              /* open flags */
  61. #if defined( __MSC__ )
  62.    #include <errno.h>
  63.    #include <sys\types.h>       /* S_IWRITE etc */
  64. #endif
  65. #include <sys\stat.h>           /* S_IWRITE etc */
  66.  
  67.  
  68. /*
  69.  * Name:    hw_fattrib
  70.  * Purpose: To determine the current file attributes.
  71.  * Date:    December 26, 1991
  72.  * Passed:  name: name of file to be checked
  73.  * Returns: use the function in the tdeasm file to get the DOS file
  74.  *          attributes.  get_fattr() returns 0 or OK if no error.
  75.  */
  76. int  hw_fattrib( char *name )
  77. {
  78. register int rc;
  79. int  fattr;
  80.  
  81.    rc = get_fattr( name, &fattr );
  82.    return( rc == OK ? rc : ERROR );
  83. }
  84.  
  85.  
  86. /*
  87.  * Name:    change_mode
  88.  * Purpose: To prompt for file access mode.
  89.  * Date:    January 11, 1992
  90.  * Passed:  name:  name of file
  91.  *          line:  line to display message
  92.  * Returns: OK if file could be changed
  93.  *          ERROR otherwise
  94.  * Notes:   function is used to change file attributes for save_as function.
  95.  */
  96. int  change_mode( char *name, int line )
  97. {
  98. int  result;
  99. int  fattr;
  100. register int rc;
  101. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  102.  
  103.    rc = OK;
  104.    result = get_fattr( name, &fattr );
  105.    if (result != OK)
  106.       rc = ERROR;
  107.    else if (result == OK && fattr & READ_ONLY) {
  108.       /*
  109.        * file is read only
  110.        */
  111.       save_screen_line( 0, line, line_buff );
  112.       /*
  113.        * file is write protected. overwrite anyway (y/n)?
  114.        */
  115.       set_prompt( main6, line );
  116.       if (get_yn( ) != A_YES)
  117.          rc = ERROR;
  118.       if (rc == OK && set_fattr( name, ARCHIVE ) != OK)
  119.          rc = ERROR;
  120.       restore_screen_line( 0, line, line_buff );
  121.    }
  122.    return( rc );
  123. }
  124.  
  125.  
  126. /*
  127.  * Name:    write_file
  128.  * Purpose: To write text to a file
  129.  *           way.
  130.  * Date:    June 5, 1991
  131.  * Passed:  name:  name of disk file or device
  132.  *          open_mode:  fopen flags to be used in open
  133.  *          file:  pointer to file structure to write
  134.  *          start: first node to write
  135.  *          end:   last node to write
  136.  *          block: write a file or a marked block
  137.  * Returns: OK, or ERROR if anything went wrong
  138.  */
  139. int  write_file( char *name, int open_mode, file_infos *file, long start,
  140.                  long end, int block )
  141. {
  142. FILE *fp;       /* file to be written */
  143. char *p;
  144. char *z = "\x1a";
  145. register int rc;
  146. int  bc;
  147. int  ec;
  148. int  len;
  149. int  write_z;
  150. int  write_eol;
  151. long number;
  152. line_list_ptr ll;
  153. char *open_string;
  154. char *eol;
  155. size_t eol_count;
  156.  
  157.    write_z = mode.control_z;
  158.    switch (open_mode) {
  159.       case APPEND :
  160.          open_string = "ab";
  161.          break;
  162.       case OVERWRITE :
  163.       default :
  164.          open_string = "wb";
  165.          break;
  166.    }
  167.    switch (file->crlf) {
  168.       case BINARY   :
  169.          eol_count = 0;
  170.          eol = "";
  171.          write_z = FALSE;
  172.          break;
  173.       case CRLF   :
  174.          eol_count = 2;
  175.          eol = "\r\n";
  176.          break;
  177.       case LF     :
  178.          eol_count = 1;
  179.          eol = "\n";
  180.          break;
  181.       default     :
  182.          assert( FALSE );
  183.    }
  184.    rc = OK;
  185.    if ((fp = fopen( name, open_string )) == NULL || ceh.flag == ERROR)
  186.       rc = ERROR;
  187.    else {
  188.       ec = bc = len = 0;
  189.       ll = file->line_list;
  190.       if (block == LINE || block == BOX || block == STREAM) {
  191.          if (g_status.marked_file == NULL)
  192.             rc = ERROR;
  193.          else
  194.             file = g_status.marked_file;
  195.          if (rc != ERROR) {
  196.             ll = file->line_list;
  197.             for (number=1; number<start && ll->next != NULL; number++)
  198.                ll = ll->next;
  199.          }
  200.          if (rc != ERROR && (block == BOX || block == STREAM)) {
  201.             bc  = file->block_bc;
  202.             ec  = file->block_ec;
  203.             len = ec + 1 - bc;
  204.          }
  205.          if (rc != ERROR  &&  block == STREAM) {
  206.             if (start == end )
  207.                block = BOX;
  208.          }
  209.       } else {
  210.          for (number=1; number<start && ll->next != NULL; number++)
  211.             ll = ll->next;
  212.       }
  213.       p = g_status.line_buff;
  214.       if (rc == OK) {
  215.          if (block == BOX) {
  216.  
  217.             assert( len >= 0 );
  218.             assert( len < MAX_LINE_LENGTH );
  219.  
  220.             for (;start <= end  &&  ll->len != EOF && rc == OK; start++) {
  221.                g_status.copied = FALSE;
  222.                load_box_buff( p, ll, bc, ec, ' ' );
  223.                if (fwrite( p, sizeof( char ), len, fp ) < (unsigned)len ||
  224.                           ceh.flag == ERROR)
  225.                   rc = ERROR;
  226.                if (rc != ERROR  && fwrite( eol, sizeof( char ), eol_count, fp )
  227.                                     < eol_count || ceh.flag == ERROR)
  228.                   rc = ERROR;
  229.                ll = ll->next;
  230.                if (ll == NULL)
  231.                   rc = ERROR;
  232.             }
  233.          } else {
  234.             for (number=start; number <= end && rc == OK && ll->len != EOF;
  235.                       number++) {
  236.                g_status.copied = FALSE;
  237.                copy_line( ll );
  238.                len = g_status.line_buff_len;
  239.                if (block == STREAM) {
  240.                   if (number == start) {
  241.                      bc = bc > len ? len : bc;
  242.                      len = len - bc;
  243.  
  244.                      assert( len >= 0 );
  245.  
  246.                      memmove( p, p + bc, len );
  247.                   } else if (number == end) {
  248.                      ++ec;
  249.                      len =  ec > len ? len : ec;
  250.                   }
  251.                }
  252.  
  253.                assert( len >= 0 );
  254.                assert( len < MAX_LINE_LENGTH );
  255.  
  256.                if (fwrite( p, sizeof( char ), len, fp ) < (unsigned)len ||
  257.                        ceh.flag == ERROR)
  258.                   rc = ERROR;
  259.  
  260.                /*
  261.                 * if a Control-Z is already at EOF, don't write another one.
  262.                 */
  263.                write_eol = TRUE;
  264.                if (number == end) {
  265.                   if (file->crlf == CRLF ||  file->crlf == LF) {
  266.                      if (len > 0  &&  *(p + len - 1) == '\x1a') {
  267.                         write_eol = FALSE;
  268.                         write_z = FALSE;
  269.                      }
  270.                   }
  271.                }
  272.  
  273.                if (write_eol == TRUE  &&  rc != ERROR  &&
  274.                      fwrite( eol, sizeof( char ), eol_count, fp ) < eol_count
  275.                      || ceh.flag == ERROR)
  276.                   rc = ERROR;
  277.                ll = ll->next;
  278.                if (ll == NULL)
  279.                   rc = ERROR;
  280.             }
  281.          }
  282.          if (rc != ERROR  &&  write_z) {
  283.             if (fwrite( z, sizeof( char ), 1, fp ) < 1 || ceh.flag == ERROR)
  284.                rc = ERROR;
  285.          }
  286.          g_status.copied = FALSE;
  287.          if (ceh.flag != ERROR) {
  288.             if (fclose( fp ) != 0)
  289.                rc = ERROR;
  290.          }
  291.       }
  292.    }
  293.    return( rc );
  294. }
  295.  
  296.  
  297. /*
  298.  * Name:    hw_save
  299.  * Purpose: To save text to a file
  300.  * Date:    November 11, 1989
  301.  * Passed:  name:  name of disk file
  302.  *          file:  pointer to file structure
  303.  *          start: first character in text buffer
  304.  *          end:   last character (+1) in text buffer
  305.  *          block: type of block defined
  306.  * Returns: OK, or ERROR if anything went wrong
  307.  */
  308. int hw_save( char *name, file_infos *file, long start, long end, int block )
  309. {
  310.    return( write_file( name, OVERWRITE, file, start, end, block ) );
  311. }
  312.  
  313.  
  314. /*
  315.  * Name:    hw_append
  316.  * Purpose: To append text to a file.
  317.  * Date:    November 11, 1989
  318.  * Passed:  name:  name of disk file
  319.  *          file:  pointer to file structure
  320.  *          start: first character in text buffer
  321.  *          end:   last character (+1) in text buffer
  322.  *          block: type of defined block
  323.  * Returns: OK, or ERROR if anything went wrong
  324.  */
  325. int hw_append( char *name, file_infos *file, long start, long end, int block )
  326. {
  327.    return( write_file( name, APPEND, file, start, end, block ) );
  328. }
  329.  
  330.  
  331. /*
  332.  * Name:    load_file
  333.  * Purpose: To load a file into the array of text pointers.
  334.  * Date:    December 1, 1992
  335.  * Passed:  name:       name of disk file
  336.  *          fp:         pointer to file structure
  337.  *          file_mode:  BINARY or TEXT
  338.  *          bin_len:    if opened in BINARY mode, length of node line
  339.  * Returns: OK, or ERROR if anything went wrong
  340.  */
  341. int  load_file( char *name, file_infos *fp, int *file_mode, int bin_len )
  342. {
  343. FILE *stream;                           /* stream to read */
  344. int  rc;
  345. char buff[MAX_COLS+2];
  346. char line_buff[(MAX_COLS+2)*2];         /* buffer for char and attribute  */
  347. text_ptr l;
  348. line_list_ptr ll;
  349. line_list_ptr temp_ll;
  350. unsigned long line_count;
  351. char *e;
  352. char *residue;
  353. int  len;
  354. int  res;
  355. size_t t1, t2;
  356. int  crlf;
  357. int  prompt_line;
  358.  
  359.    /*
  360.     * initialize the counters and pointers
  361.     */
  362.    rc = OK;
  363.    len = 1;
  364.    line_count = 0;
  365.    res = 0;
  366.    residue = g_status.line_buff;
  367.    prompt_line = g_display.nlines;
  368.    fp->length  = 0;
  369.    fp->undo_count = 0;
  370.    fp->undo_top = fp->undo_bot = NULL;
  371.    fp->line_list_end = fp->line_list = NULL;
  372.    ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  373.  
  374.    if (ll != NULL) {
  375.       ll->dirty = FALSE;
  376.       ll->len   = EOF;
  377.       ll->line  = NULL;
  378.       ll->next  = ll->prev = NULL;
  379.       fp->undo_top = fp->undo_bot = ll;
  380.    }
  381.  
  382.    ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  383.  
  384.    if (ll != NULL) {
  385.       ll->dirty = FALSE;
  386.       ll->len   = EOF;
  387.       ll->line  = NULL;
  388.       ll->next  = ll->prev = NULL;
  389.       fp->line_list_end = fp->line_list = ll;
  390.    }
  391.  
  392.    if ((stream = fopen( name, "rb" )) == NULL || ceh.flag == ERROR ||
  393.          rc == ERROR) {
  394.       /*
  395.        * file not found or error loading file
  396.        */
  397.       combine_strings( buff, main7a, name, main7b );
  398.       save_screen_line( 0, prompt_line, line_buff );
  399.       set_prompt( buff, prompt_line );
  400.       getkey( );
  401.       restore_screen_line( 0, prompt_line, line_buff );
  402.       if (fp->line_list != NULL)
  403.          my_free( fp->line_list );
  404.       if (fp->undo_top != NULL)
  405.          my_free( fp->undo_top );
  406.       rc = ERROR;
  407.    } else {
  408.       if (*file_mode == BINARY) {
  409.          mode.trailing = FALSE;
  410.          crlf = BINARY;
  411.          if (bin_len < 0  ||  bin_len > READ_LENGTH)
  412.             bin_len = DEFAULT_BIN_LENGTH;
  413.          for (; rc == OK;) {
  414.             t1 = fread( g_status.line_buff, sizeof(char), bin_len, stream );
  415.             if (ferror( stream )  ||  ceh.flag == ERROR) {
  416.                combine_strings( buff, "error reading file '", name, "'" );
  417.                error( WARNING, prompt_line, buff );
  418.                rc = ERROR;
  419.             } else if (t1) {
  420.  
  421.                assert( t1 < MAX_LINE_LENGTH );
  422.  
  423.                l = (text_ptr)my_malloc( t1 * sizeof(char), &rc );
  424.                temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  425.  
  426.                if (rc != ERROR) {
  427.  
  428.                   /*
  429.                    * if everything is everything, copy from io buff to
  430.                    *   dynamic mem.
  431.                    */
  432.                   if (t1 > 0)
  433.                      _fmemcpy( l, g_status.line_buff, t1 );
  434.  
  435.                   ++line_count;
  436.                   temp_ll->line = l;
  437.                   temp_ll->dirty = FALSE;
  438.                   temp_ll->len  = t1;
  439.                   insert_node( fp, ll, temp_ll );
  440.                   ll = temp_ll;
  441.                } else
  442.                   rc = show_file_2big( name, prompt_line, temp_ll, l );
  443.             } else
  444.                break;
  445.          }
  446.       } else {
  447.          crlf = LF;
  448.          for (; rc == OK;) {
  449.             t1 = fread( g_status.line_buff, sizeof(char), READ_LENGTH, stream );
  450.             if (ferror( stream )  ||  ceh.flag == ERROR) {
  451.                combine_strings( buff, "error reading file '", name, "'" );
  452.                error( WARNING, prompt_line, buff );
  453.                rc = ERROR;
  454.             } else {
  455.  
  456.                /*
  457.                 * "e" walks down io buffer 1 looking for end of lines.  "t1"
  458.                 *   keeps count of number of characters in io buffer 1.
  459.                 */
  460.                e = g_status.line_buff;
  461.                while (t1 && rc == OK) {
  462.  
  463.                   /*
  464.                    * while "t1" is not zero and "len" is less than max line length,
  465.                    *   let "e" walk down the buffer until it find <LF>.
  466.                    */
  467.                   for (; t1 && len < READ_LENGTH &&  *e != '\n'; len++, e++, t1--);
  468.  
  469.                   /*
  470.                    * if "t1" is not zero, we either found a <LF> or the line
  471.                    *   length max'd out.
  472.                    */
  473.                   if (t1  ||  len >= READ_LENGTH) {
  474.  
  475.                      if (len > 1 && *e == '\n') {
  476.                         if (len - res == 1) {
  477.                            if (*(residue + res - 1) == '\r') {
  478.                               --len;
  479.                               --res;
  480.                               crlf = CRLF;
  481.                            }
  482.                         } else {
  483.                            if (*(e - 1) == '\r') {
  484.                               --len;
  485.                               crlf = CRLF;
  486.                            }
  487.                         }
  488.                      }
  489.                      if (len > 0)
  490.                         --len;
  491.  
  492.                      assert( len >= 0 );
  493.                      assert( len < MAX_LINE_LENGTH );
  494.  
  495.                      /*
  496.                       * allocate space for relocatable array of line pointers and
  497.                       *   allocate space for the line we just read.
  498.                       */
  499.                      l = (text_ptr)my_malloc( len * sizeof(char), &rc );
  500.                      temp_ll =
  501.                        (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  502.  
  503.                      if (rc != ERROR) {
  504.  
  505.                         /*
  506.                          * if everything is everything, copy from io buff to
  507.                          *   dynamic mem.  "residue" keeps up with the beginning
  508.                          *   of line in io buffer.
  509.                          */
  510.                         if (res > 0) {
  511.  
  512.                            assert( res >= 0 );
  513.                            assert( len - res >= 0 );
  514.  
  515.                            if (res > 0)
  516.                               _fmemcpy( l, residue, res );
  517.                            if (len - res > 0)
  518.                               _fmemcpy( l + res, g_status.line_buff, len - res );
  519.                            res = 0;
  520.                         } else
  521.                            if (len > 0)
  522.                               _fmemcpy( l, residue, len );
  523.  
  524.                         ++line_count;
  525.                         temp_ll->line = l;
  526.                         temp_ll->dirty = FALSE;
  527.                         temp_ll->len  = len;
  528.                         insert_node( fp, ll, temp_ll );
  529.                         ll = temp_ll;
  530.  
  531.                         /*
  532.                          * reset io buffer pointers and counters.
  533.                          */
  534.                         len = 1;
  535.                         if (t1 == 0)
  536.                            residue = g_status.tabout_buff;
  537.                         else {
  538.                            t1--;
  539.                            residue =  t1 == 0 ? g_status.tabout_buff : ++e;
  540.                         }
  541.                      } else
  542.                         rc = show_file_2big( name, prompt_line, temp_ll, l );
  543.                   } else if (len < READ_LENGTH ) {
  544.                      if (!feof( stream ))
  545.                         res = len - 1;
  546.                   } else {
  547.                      error( WARNING, prompt_line, "FRANK: error reading file!" );
  548.                      rc = ERROR;
  549.                   }
  550.                }
  551.             }
  552.  
  553.             if (rc != OK)
  554.                break;
  555.  
  556.             /*
  557.              * we may have read all lines that end in '\n', but there may
  558.              *   be some residue after the last '\n'.  ^Z is a good example.
  559.              */
  560.             if (feof( stream )) {
  561.                if (len > 1) {
  562.                   --len;
  563.                   if (t1 == 0)
  564.                      --e;
  565.  
  566.                   assert( len >= 0 );
  567.                   assert( len < MAX_LINE_LENGTH );
  568.  
  569.                   /*
  570.                    * allocate space for relocatable array of line pointers and
  571.                    *   allocate space for the line we just read.
  572.                    */
  573.                   l = (text_ptr)my_malloc( len * sizeof(char), &rc );
  574.                   temp_ll =
  575.                        (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  576.  
  577.                   if (rc != ERROR) {
  578.  
  579.                      /*
  580.                       * if everything is everything, copy from io buff to
  581.                       *   dynamic mem.  "residue" keeps up with the beginning
  582.                       *   of line in io buffer.
  583.                       */
  584.                      if (res > 0) {
  585.  
  586.                         assert( res >= 0 );
  587.                         assert( res < MAX_LINE_LENGTH);
  588.                         assert( len - res >= 0 );
  589.                         assert( len - res < MAX_LINE_LENGTH);
  590.  
  591.                         if (res > 0 )
  592.                            _fmemcpy( l, residue, res );
  593.                         if (len - res > 0)
  594.                            _fmemcpy( l + res, g_status.line_buff, len - res );
  595.                      } else
  596.                         if (len > 0)
  597.                            _fmemcpy( l, residue, len );
  598.                      ++line_count;
  599.                      temp_ll->line = l;
  600.                      temp_ll->dirty = FALSE;
  601.                      temp_ll->len  = len;
  602.                      insert_node( fp, ll, temp_ll );
  603.                   } else
  604.                      rc = show_file_2big( name, prompt_line, temp_ll, l );
  605.                }
  606.                break;
  607.             }
  608.  
  609.             t2 = fread( g_status.tabout_buff, sizeof(char), READ_LENGTH, stream );
  610.             if (ferror( stream )  ||  ceh.flag == ERROR) {
  611.                combine_strings( buff, "error reading file '", name, "'" );
  612.                error( WARNING, prompt_line, buff );
  613.                rc = ERROR;
  614.             } else if (rc == OK) {
  615.                e = g_status.tabout_buff;
  616.                while (t2 && rc == OK) {
  617.                   for (; t2 && len < READ_LENGTH &&  *e != '\n'; len++, e++, t2--);
  618.                   if (t2  ||  len >= READ_LENGTH) {
  619.  
  620.                      if (len > 1 && *e == '\n') {
  621.                         if (len - res == 1) {
  622.                            if (*(residue + res - 1) == '\r') {
  623.                               --len;
  624.                               --res;
  625.                               crlf = CRLF;
  626.                            }
  627.                         } else {
  628.                            if (*(e - 1) == '\r') {
  629.                               --len;
  630.                               crlf = CRLF;
  631.                            }
  632.                         }
  633.                      }
  634.                      if (len > 0)
  635.                         --len;
  636.  
  637.                      assert( len >= 0 );
  638.                      assert( len < MAX_LINE_LENGTH );
  639.  
  640.                      l = (text_ptr)my_malloc( len * sizeof(char), &rc );
  641.                      temp_ll =
  642.                        (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  643.  
  644.                      if (rc != ERROR) {
  645.                         if (res > 0) {
  646.  
  647.                            assert( res >= 0 );
  648.                            assert( res < MAX_LINE_LENGTH);
  649.                            assert( len - res >= 0 );
  650.                            assert( len - res < MAX_LINE_LENGTH);
  651.  
  652.                            if (res > 0)
  653.                               _fmemcpy( l, residue, res );
  654.                            if (len - res > 0)
  655.                               _fmemcpy( l+res, g_status.tabout_buff, len - res );
  656.                            res = 0;
  657.                         } else
  658.                            if (len > 0)
  659.                               _fmemcpy( l, residue, len );
  660.  
  661.                         ++line_count;
  662.                         temp_ll->line = l;
  663.                         temp_ll->dirty = FALSE;
  664.                         temp_ll->len  = len;
  665.                         insert_node( fp, ll, temp_ll );
  666.                         ll = temp_ll;
  667.  
  668.                         len = 1;
  669.                         if (t2 == 0)
  670.                            residue = g_status.line_buff;
  671.                         else {
  672.                            t2--;
  673.                            residue =  t2 == 0 ? g_status.line_buff : ++e;
  674.                         }
  675.                      } else
  676.                         rc = show_file_2big( name, prompt_line, temp_ll, l );
  677.                   } else if (len < READ_LENGTH) {
  678.                      if (!feof( stream ))
  679.                         res = len - 1;
  680.                   } else {
  681.                      error( WARNING, prompt_line, "FRANK: error reading file!" );
  682.                      rc = ERROR;
  683.                   }
  684.                }
  685.             }
  686.  
  687.             if (rc != ERROR  &&  feof( stream )) {
  688.                if (len > 1) {
  689.                   --len;
  690.                   if (t2 == 0)
  691.                      --e;
  692.  
  693.                   assert( len >= 0 );
  694.                   assert( len < MAX_LINE_LENGTH );
  695.  
  696.                   l = (text_ptr)my_malloc( len * sizeof(char), &rc );
  697.                   temp_ll =
  698.                        (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  699.  
  700.                   if (rc != ERROR) {
  701.                      if (res > 0) {
  702.  
  703.                         assert( res >= 0 );
  704.                         assert( res < MAX_LINE_LENGTH);
  705.                         assert( len - res >= 0 );
  706.                         assert( len - res < MAX_LINE_LENGTH);
  707.  
  708.                         if (res > 0)
  709.                            _fmemcpy( l, residue, res );
  710.                         if (len - res > 0)
  711.                            _fmemcpy( l+res, g_status.tabout_buff, len - res );
  712.                      } else
  713.                         if (len > 0)
  714.                            _fmemcpy( l, residue, len );
  715.  
  716.                      ++line_count;
  717.                      temp_ll->line = l;
  718.                      temp_ll->dirty = FALSE;
  719.                      temp_ll->len  = len;
  720.                      insert_node( fp, ll, temp_ll );
  721.                   } else
  722.                      rc = show_file_2big( name, prompt_line, temp_ll, l );
  723.                }
  724.                break;
  725.             }
  726.          }
  727.          *file_mode = crlf;
  728.       }
  729.  
  730.       /*
  731.        * close the file
  732.        */
  733.       fp->length = line_count;
  734.    }
  735.    if (stream != NULL)
  736.       fclose( stream );
  737.    return( rc );
  738. }
  739.  
  740.  
  741. /*
  742.  * Name:    insert_node
  743.  * Purpose: To insert a node into a double linked list
  744.  * Date:    December 1, 1992
  745.  * Passed:  fp:  pointer to file structure that owns the double linked list
  746.  *          current: pointer to current node in double linked list
  747.  *          new:     pointer to new node to insert into double linked list
  748.  * Notes:   if the current list pointer is the last node in the list, insert
  749.  *            new code behind current node.
  750.  */
  751. void insert_node( file_infos *fp, line_list_ptr current, line_list_ptr new )
  752. {
  753.  
  754.    /*
  755.     * standard double linked list insert
  756.     */
  757.    if (current->next != NULL) {
  758.       current->next->prev = new;
  759.       new->next = current->next;
  760.       current->next = new;
  761.       new->prev = current;
  762.    /*
  763.     * if the current node is the NULL node, insert the new node behind current
  764.     */
  765.    } else {
  766.       new->next = current;
  767.       if (current->prev != NULL)
  768.          current->prev->next = new;
  769.       new->prev = current->prev;
  770.       current->prev = new;
  771.       if (new->prev == NULL)
  772.          fp->line_list = new;
  773.    }
  774. }
  775.  
  776.  
  777. /*
  778.  * Name:    show_file_2big
  779.  * Purpose: tell user we ran out of room loading file
  780.  * Date:    December 1, 1992
  781.  * Passed:  name:  name of disk file
  782.  *          line:  line to display messages
  783.  *          ll:    double linked list pointer
  784.  *          t:     text line pointer
  785.  * Returns: WARNING
  786.  * Notes:   one or both of the malloc requests overflowed the heap.  free the
  787.  *            dynamic if allocated.
  788.  */
  789. int  show_file_2big( char *name, int prompt_line, line_list_ptr ll, text_ptr t )
  790. {
  791. char buff[MAX_COLS+2];
  792.  
  793.    combine_strings( buff, main10a, name, main10b );
  794.    error( WARNING, prompt_line, buff );
  795.    if (t != NULL)
  796.       my_free( t );
  797.    if (ll != NULL)
  798.       my_free( ll );
  799.    return( WARNING );
  800. }
  801.  
  802.  
  803. /*
  804.  * Name:    backup_file
  805.  * Purpose: To make a back-up copy of current file.
  806.  * Date:    June 5, 1991
  807.  * Passed:  window:  current window pointer
  808.  */
  809. int  backup_file( WINDOW *window )
  810. {
  811. char *old_line_buff;
  812. char *old_tabout_buff;
  813. int  old_line_buff_len;
  814. int  old_tabout_buff_len;
  815. int  old_copied;
  816. int  rc;
  817. file_infos *file;
  818.  
  819.    rc = OK;
  820.    file = window->file_info;
  821.    if (file->backed_up == FALSE  &&  file->modified == TRUE) {
  822.       old_copied = g_status.copied;
  823.       old_line_buff_len = g_status.line_buff_len;
  824.       old_line_buff = calloc( MAX_LINE_LENGTH, sizeof(char) );
  825.       old_tabout_buff_len = g_status.tabout_buff_len;
  826.       old_tabout_buff = calloc( MAX_LINE_LENGTH, sizeof(char) );
  827.  
  828.       if (old_line_buff != NULL  &&  old_tabout_buff != NULL) {
  829.          memcpy( old_line_buff, g_status.line_buff, MAX_LINE_LENGTH );
  830.          memcpy( old_tabout_buff, g_status.tabout_buff, MAX_LINE_LENGTH );
  831.          if ((rc = save_backup( window )) != ERROR)
  832.             file->backed_up = TRUE;
  833.          else
  834.             rc = ERROR;
  835.          memcpy( g_status.line_buff, old_line_buff, MAX_LINE_LENGTH );
  836.          memcpy( g_status.tabout_buff, old_tabout_buff, MAX_LINE_LENGTH );
  837.          g_status.line_buff_len = old_line_buff_len;
  838.          g_status.tabout_buff_len = old_tabout_buff_len;
  839.          g_status.copied = old_copied;
  840.       } else {
  841.          error( WARNING, window->bottom_line, main4 );
  842.          rc = ERROR;
  843.       }
  844.       if (old_line_buff != NULL)
  845.          free( old_line_buff );
  846.       if (old_tabout_buff != NULL)
  847.          free( old_tabout_buff );
  848.    }
  849.    return( rc );
  850. }
  851.  
  852.  
  853. /*
  854.  * Name:    edit_file
  855.  * Purpose: To allocate space for a new file structure and set up some
  856.  *           of the relevant fields.
  857.  * Date:    June 5, 1991
  858.  * Passed:  name:  name of the file to edit
  859.  *          file_mode:  BINARY or TEXT
  860.  *          bin_length: if opened in BINARY mode, length of binary lines
  861.  * Returns: OK if file structure could be created
  862.  *          ERROR if out of memory
  863.  */
  864. int  edit_file( char *name, int file_mode, int bin_length )
  865. {
  866. int  rc;        /* return code */
  867. int  existing;
  868. int  line;
  869. int  rcol;
  870. register file_infos *file; /* file structure for file belonging to new window */
  871. file_infos *fp;
  872. long found_line;
  873. line_list_ptr ll;
  874. line_list_ptr temp_ll;
  875.  
  876.    line = g_display.nlines;
  877.    rc = OK;
  878.    /*
  879.     * allocate a file structure for the new file
  880.     */
  881.    file = (file_infos *)calloc( 1, sizeof(file_infos) );
  882.    if (file == NULL) {
  883.       error( WARNING, line, main4 );
  884.       rc = ERROR;
  885.    }
  886.    existing = FALSE;
  887.    if (rc == OK  &&  hw_fattrib( name ) == OK) {
  888.       existing = TRUE;
  889.       /*
  890.        * g_status.temp_end is set last character read in file
  891.        */
  892.  
  893.       if (g_status.command != DefineGrep  &&
  894.           g_status.command != DefineRegXGrep  &&
  895.           g_status.command != RepeatGrep)
  896.          rc = load_file( name, file, &file_mode, bin_length );
  897.       else {
  898.          if (g_status.sas_defined) {
  899.             rc = load_file( name, file, &file_mode, bin_length );
  900.             if (rc != ERROR) {
  901.                found_line = 1L;
  902.                rcol = 0;
  903.                if (g_status.sas_search_type == BOYER_MOORE)
  904.                   ll = search_forward( file->line_list, &found_line,
  905.                                        (size_t *)&rcol );
  906.                else
  907.                   ll = regx_search_forward( file->line_list, &found_line,
  908.                                             &rcol );
  909.                if (ll == NULL)
  910.                   rc = ERROR;
  911.                else {
  912.                   g_status.sas_rline = found_line;
  913.                   g_status.sas_rcol  = rcol;
  914.                   g_status.sas_ll    = ll;
  915.                }
  916.             }
  917.          } else
  918.             rc = ERROR;
  919.       }
  920.    } else {
  921.       if (ceh.flag == ERROR)
  922.          rc = ERROR;
  923.       else {
  924.          existing = FALSE;
  925.          file->length = 0l;
  926.          file->undo_top = file->undo_bot = NULL;
  927.          file->line_list_end = file->line_list = NULL;
  928.          file->undo_count = 0;
  929.          ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  930.          if (ll != NULL) {
  931.             ll->line  = NULL;
  932.             ll->next  = ll->prev = NULL;
  933.             ll->dirty = FALSE;
  934.             ll->len   = EOF;
  935.             file->undo_top = file->undo_bot = ll;
  936.          } else
  937.             rc = ERROR;
  938.  
  939.          ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  940.          if (ll != NULL) {
  941.             ll->line = NULL;
  942.             ll->next = ll->prev = NULL;
  943.             ll->dirty = FALSE;
  944.             ll->len   = EOF;
  945.             file->line_list_end = file->line_list = ll;
  946.          } else
  947.             rc = ERROR;
  948.          if (rc == ERROR) {
  949.             if (file->undo_top != NULL)
  950.                my_free( file->undo_top );
  951.             if (file->line_list != NULL)
  952.                my_free( file->line_list );
  953.          } else
  954.             if (file_mode == TEXT)
  955.                file_mode = CRLF;
  956.       }
  957.    }
  958.  
  959.    if (rc != ERROR) {
  960.       /*
  961.        * add file into list
  962.        */
  963.       file->prev = NULL;
  964.       file->next = NULL;
  965.       if (g_status.file_list == NULL)
  966.          g_status.file_list = file;
  967.       else {
  968.          fp = g_status.current_file;
  969.          file->prev = fp;
  970.          if (fp->next)
  971.             fp->next->prev = file;
  972.          file->next = fp->next;
  973.          fp->next = file;
  974.       }
  975.  
  976.       /*
  977.        * set up all the info we need to know about a file.
  978.        */
  979.  
  980.       assert( file_mode == CRLF  ||  file_mode == LF  ||  file_mode == BINARY );
  981.       assert( strlen( name ) < MAX_COLS );
  982.  
  983.       strcpy( file->file_name, name );
  984.       get_fattr( name, (int *)&file->file_attrib );
  985.       file->block_type  = NOTMARKED;
  986.       file->block_br    = file->block_er = 0l;
  987.       file->block_bc    = file->block_ec = 0;
  988.       file->ref_count   = 0;
  989.       file->modified    = FALSE;
  990.       file->backed_up   = FALSE;
  991.       file->new_file    = !existing;
  992.       file->next_letter = 'a';
  993.       file->file_no     = ++g_status.file_count;
  994.       file->crlf        = file_mode;
  995.       g_status.current_file = file;
  996.       make_backup_fname( file );
  997.    } else if (file != NULL) {
  998.       /*
  999.        * free the line pointers and linked list of line pointers.
  1000.        */
  1001.       ll = file->undo_top;
  1002.       while (ll != NULL) {
  1003.          temp_ll = ll->next;
  1004.          if (ll->line != NULL)
  1005.             my_free( ll->line );
  1006.          my_free( ll );
  1007.          ll = temp_ll;
  1008.       }
  1009.  
  1010.       ll = file->line_list;
  1011.       while (ll != NULL) {
  1012.          temp_ll = ll->next;
  1013.          if (ll->line != NULL)
  1014.             my_free( ll->line );
  1015.          my_free( ll );
  1016.          ll = temp_ll;
  1017.       }
  1018.  
  1019. #if defined( __MSC__ )
  1020.       _fheapmin( );
  1021. #endif
  1022.  
  1023.       free( file );
  1024.    }
  1025.    return( rc );
  1026. }
  1027.  
  1028.  
  1029. /*
  1030.  * Name:    edit_another_file
  1031.  * Purpose: Bring in another file to editor.
  1032.  * Date:    June 5, 1991
  1033.  * Passed:  window:  pointer to current window
  1034.  * Notes:   New window replaces old window.  Old window becomes invisible.
  1035.  */
  1036. int  edit_another_file( WINDOW *window )
  1037. {
  1038. char fname[MAX_COLS];           /* new name for file */
  1039. char spdrive[_MAX_DRIVE];       /* splitpath drive buff */
  1040. char spdir[_MAX_DIR];           /* splitpath dir buff */
  1041. char spname[_MAX_FNAME];        /* splitpath fname buff */
  1042. char spext[_MAX_EXT];           /* splitpath ext buff */
  1043. register WINDOW *win;           /* put window pointer in a register */
  1044. int  rc;
  1045. int  file_mode;
  1046. int  bin_length;
  1047.  
  1048.    win = window;
  1049.    entab_linebuff( );
  1050.    if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1051.       return( ERROR );
  1052.    /*
  1053.     * read in name, no default
  1054.     */
  1055.    fname[0] = '\0';
  1056.    /*
  1057.     * file name to edit
  1058.     */
  1059.    if ((rc = get_name( ed15, win->bottom_line, fname,
  1060.                  g_display.message_color )) == OK  &&  *fname != '\0') {
  1061.       file_mode = TEXT;
  1062.       bin_length = 0;
  1063.  
  1064.       assert( strlen( fname ) <= MAX_COLS );
  1065.  
  1066.       _splitpath( fname, spdrive, spdir, spname, spext );
  1067.       if (stricmp( spext, ".exe" ) == 0  ||  stricmp( spext, ".com" ) == 0) {
  1068.          file_mode = BINARY;
  1069.          bin_length = g_status.file_chunk;
  1070.       }
  1071.       rc = attempt_edit_display( fname, LOCAL, file_mode, bin_length );
  1072.       if (rc == OK)
  1073.          show_avail_mem( );
  1074.    }
  1075.    return( rc );
  1076. }
  1077.  
  1078.  
  1079. /*
  1080.  * Name:    edit_next_file
  1081.  * Purpose: edit next file on command line.
  1082.  * Date:    January 6, 1992
  1083.  * Passed:  window:  pointer to current window
  1084.  * Notes:   New window replaces old window.  Old window becomes invisible.
  1085.  */
  1086. int  edit_next_file( WINDOW *window )
  1087. {
  1088. char name[MAX_COLS];            /* new name for file */
  1089. char spdrive[_MAX_DRIVE];       /* splitpath drive buff */
  1090. char spdir[_MAX_DIR];           /* splitpath dir buff */
  1091. char spname[_MAX_FNAME];        /* splitpath fname buff */
  1092. char spext[_MAX_EXT];           /* splitpath ext buff */
  1093. int  file_mode;
  1094. int  bin_length;
  1095. int  i;
  1096. int  update_type;
  1097. register int rc = ERROR;
  1098. register WINDOW *win;           /* put window pointer in a register */
  1099.  
  1100.    win = window;
  1101.    update_type = win == NULL ? GLOBAL : LOCAL;
  1102.    if (g_status.arg < g_status.argc) {
  1103.       if (win != NULL) {
  1104.          entab_linebuff( );
  1105.          if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1106.             return( ERROR );
  1107.       }
  1108.  
  1109.       /*
  1110.        * while we haven't found a valid file, search thru the command
  1111.        * line path.
  1112.        * we may have an invalid file name when we finish matching all
  1113.        * files according to a pattern.  then, we need to go to the next
  1114.        * command line argument if it exists.
  1115.        */
  1116.       while (rc == ERROR && g_status.arg < g_status.argc) {
  1117.  
  1118.          /*
  1119.           * if we haven't starting searching for a file, check to see if
  1120.           * the file is a valid file name.  if no file is found, then let's
  1121.           * see if we can find according to a search pattern.
  1122.           */
  1123.          if (g_status.found_first == FALSE) {
  1124.  
  1125.             assert( strlen( g_status.argv[g_status.arg] ) < MAX_COLS );
  1126.  
  1127.             strcpy( name, g_status.argv[g_status.arg] );
  1128.             rc = get_fattr( name, &i );
  1129.  
  1130.             /*
  1131.              * a new or blank file generates a return code of 2.
  1132.              * a pattern with wild cards generates a return code of 3.
  1133.              */
  1134.             if (rc == OK || rc == 2) {
  1135.                ++g_status.arg;
  1136.                rc = OK;
  1137.  
  1138.             /*
  1139.              * if we get this far, we got an invalid path name.
  1140.              *  let's try to find a matching file name using pattern.
  1141.              */
  1142.             } else if (rc != ERROR) {
  1143.                rc = my_findfirst( &g_status.dta, name, NORMAL | READ_ONLY |
  1144.                                HIDDEN | SYSTEM | ARCHIVE );
  1145.  
  1146.                /*
  1147.                 * if we found a file using wildcard characters,
  1148.                 * set the g_status.found_first flag to true so we can
  1149.                 * find the next matching file.  we need to save the
  1150.                 * pathname stem so we know which directory we are working in.
  1151.                 */
  1152.                if (rc == OK) {
  1153.                   g_status.found_first = TRUE;
  1154.                   i = strlen( name ) - 1;
  1155.                   while (i >= 0) {
  1156.                      if (name[i] == ':' || name[i] == '\\')
  1157.                         break;
  1158.                      --i;
  1159.                   }
  1160.                   name[++i] = '\0';
  1161.  
  1162.                   assert( strlen( name ) < MAX_COLS );
  1163.  
  1164.                   strcpy( g_status.path, name );
  1165.                   strcpy( name, g_status.path );
  1166.                   strcat( name, g_status.dta.name );
  1167.                } else {
  1168.                   ++g_status.arg;
  1169.                   if (win != NULL)
  1170.                      /*
  1171.                       * invalid path or file name
  1172.                       */
  1173.                      error( WARNING, win->bottom_line, win8 );
  1174.                }
  1175.             } else if (rc == ERROR)
  1176.                ++g_status.arg;
  1177.          } else {
  1178.  
  1179.             /*
  1180.              * we already found one file with wild card characters,
  1181.              * find the next matching file.
  1182.              */
  1183.             rc = my_findnext( &g_status.dta );
  1184.             if (rc == OK) {
  1185.  
  1186.                assert( strlen( g_status.path ) + strlen( g_status.dta.name )
  1187.                            < MAX_COLS );
  1188.  
  1189.                strcpy( name, g_status.path );
  1190.                strcat( name, g_status.dta.name );
  1191.             } else {
  1192.                g_status.found_first = FALSE;
  1193.                ++g_status.arg;
  1194.             }
  1195.          }
  1196.  
  1197.          /*
  1198.           * if everything is everything so far, set up the file
  1199.           * and window structures and bring the file into the editor.
  1200.           */
  1201.          if (rc == OK) {
  1202.             file_mode = g_status.file_mode;
  1203.             bin_length = g_status.file_chunk;
  1204.  
  1205.             assert( strlen( name ) <= MAX_COLS );
  1206.  
  1207.             _splitpath( name, spdrive, spdir, spname, spext );
  1208.             if (stricmp( spext, ".exe" ) == 0 || stricmp( spext, ".com" ) == 0)
  1209.                file_mode = BINARY;
  1210.             rc = attempt_edit_display( name, update_type, file_mode, bin_length );
  1211.             if (rc == OK)
  1212.                show_avail_mem( );
  1213.          }
  1214.  
  1215.          /*
  1216.           * either there are no more matching files or we had an
  1217.           * invalid file name, set rc to ERROR and let's look at the
  1218.           * next file name or pattern on the command line.
  1219.           */
  1220.          else
  1221.             rc = ERROR;
  1222.       }
  1223.    }
  1224.    if (rc == ERROR  &&  g_status.arg >= g_status.argc  &&  win != NULL)
  1225.       /*
  1226.        * no more files to load
  1227.        */
  1228.       error( WARNING, win->bottom_line, win9 );
  1229.    return( rc );
  1230. }
  1231.  
  1232.  
  1233. /*
  1234.  * Name:    search_and_seize
  1235.  * Purpose: search files for a pattern
  1236.  * Date:    October 31, 1992
  1237.  * Passed:  window:  pointer to current window
  1238.  * Notes:   New window replaces old window.  Old window becomes invisible.
  1239.  */
  1240. int  search_and_seize( WINDOW *window )
  1241. {
  1242. char name[MAX_COLS];            /* new name for file */
  1243. char searching[MAX_COLS];       /* buffer for displaying file name */
  1244. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1245. char spdrive[_MAX_DRIVE];       /* splitpath drive buff */
  1246. char spdir[_MAX_DIR];           /* splitpath dir buff */
  1247. char spname[_MAX_FNAME];        /* splitpath fname buff */
  1248. char spext[_MAX_EXT];           /* splitpath ext buff */
  1249. int  file_mode;
  1250. int  bin_length;
  1251. int  i;
  1252. int  update_type;
  1253. char *tokens;
  1254. register int rc = ERROR;
  1255. register WINDOW *win;           /* put window pointer in a register */
  1256. int  bottom_line;
  1257.  
  1258.    win = window;
  1259.    update_type = win == NULL ? GLOBAL : LOCAL;
  1260.    if (update_type == LOCAL) {
  1261.       if (!g_status.sas_defined ||  g_status.command == DefineGrep ||
  1262.                               g_status.command == DefineRegXGrep) {
  1263.  
  1264.          /*
  1265.           * prompt for the search pattern and the seize path.
  1266.           *   initialize all this stuff.
  1267.           */
  1268.          if (g_status.command == DefineGrep)
  1269.             g_status.sas_search_type = BOYER_MOORE;
  1270.          else
  1271.             g_status.sas_search_type = REG_EXPRESSION;
  1272.  
  1273.          if (g_status.sas_search_type == BOYER_MOORE) {
  1274.             *sas_bm.pattern = '\0';
  1275.             if (get_name( win16a, win->bottom_line, (char *)sas_bm.pattern,
  1276.                              g_display.message_color ) == ERROR)
  1277.                return( ERROR );
  1278.             if (*sas_bm.pattern == '\0')
  1279.                return( ERROR );
  1280.          } else {
  1281.             *sas_regx.pattern = '\0';
  1282.             if (get_name( win16b, win->bottom_line, (char *)sas_regx.pattern,
  1283.                              g_display.message_color ) == ERROR)
  1284.                return( ERROR );
  1285.             if (*sas_regx.pattern == '\0')
  1286.                return( ERROR );
  1287.             else
  1288.                strcpy( (char *)regx.pattern, (char *)sas_regx.pattern );
  1289.          }
  1290.          *g_status.sas_tokens = '\0';
  1291.          if (get_name( win17, win->bottom_line, g_status.sas_tokens,
  1292.                           g_display.message_color ) == ERROR)
  1293.             return( ERROR );
  1294.          i = 0;
  1295.          tokens = strtok( g_status.sas_tokens, SAS_DELIMITERS );
  1296.          while (tokens != NULL) {
  1297.             g_status.sas_arg_pointers[i++] = tokens;
  1298.             tokens = strtok( NULL, SAS_DELIMITERS );
  1299.          }
  1300.          if (i == 0)
  1301.             return( ERROR );
  1302.          g_status.sas_arg_pointers[i] = NULL;
  1303.          g_status.sas_argc = i;
  1304.          g_status.sas_arg = 0;
  1305.          g_status.sas_argv = g_status.sas_arg_pointers;
  1306.          g_status.sas_found_first = FALSE;
  1307.          if (g_status.command == DefineGrep) {
  1308.             g_status.sas_defined = TRUE;
  1309.             bm.search_defined = sas_bm.search_defined = OK;
  1310.             build_boyer_array( );
  1311.          } else {
  1312.             i = build_nfa( );
  1313.             if (i == OK) {
  1314.                g_status.sas_defined = TRUE;
  1315.                regx.search_defined = sas_regx.search_defined = OK;
  1316.             } else
  1317.                g_status.sas_defined = FALSE;
  1318.          }
  1319.       }
  1320.       bottom_line = win->bottom_line;
  1321.    } else
  1322.       bottom_line = g_display.nlines;
  1323.    if (g_status.sas_defined && g_status.sas_arg < g_status.sas_argc) {
  1324.       if (win != NULL) {
  1325.          entab_linebuff( );
  1326.          un_copy_line( win->ll, win, TRUE );
  1327.       }
  1328.  
  1329.       /*
  1330.        * while we haven't found a valid file, search thru the command
  1331.        * line path.
  1332.        * we may have an invalid file name when we finish matching all
  1333.        * files according to a pattern.  then, we need to go to the next
  1334.        * command line argument if it exists.
  1335.        */
  1336.       while (rc == ERROR && g_status.sas_arg < g_status.sas_argc) {
  1337.  
  1338.          /*
  1339.           * if we haven't starting searching for a file, check to see if
  1340.           * the file is a valid file name.  if no file is found, then let's
  1341.           * see if we can find according to a search pattern.
  1342.           */
  1343.          if (g_status.sas_found_first == FALSE) {
  1344.  
  1345.             assert( strlen( g_status.sas_argv[g_status.sas_arg] ) < MAX_COLS );
  1346.  
  1347.             strcpy( name, g_status.sas_argv[g_status.sas_arg] );
  1348.             rc = get_fattr( name, &i );
  1349.  
  1350.             /*
  1351.              * a new or blank file generates a return code of 2.
  1352.              * a pattern with wild cards generates a return code of 3.
  1353.              */
  1354.             if (rc == OK || rc == 2) {
  1355.                ++g_status.sas_arg;
  1356.                rc = OK;
  1357.  
  1358.             /*
  1359.              * if we get this far, we got an invalid path name.
  1360.              *  let's try to find a matching file name using pattern.
  1361.              */
  1362.             } else if (rc != ERROR) {
  1363.                rc = my_findfirst( &g_status.sas_dta, name, NORMAL | READ_ONLY |
  1364.                                HIDDEN | SYSTEM | ARCHIVE );
  1365.  
  1366.                /*
  1367.                 * if we found a file using wildcard characters,
  1368.                 * set the g_status.sas_found_first flag to true so we can
  1369.                 * find the next matching file.  we need to save the
  1370.                 * pathname stem so we know which directory we are working in.
  1371.                 */
  1372.                if (rc == OK) {
  1373.                   g_status.sas_found_first = TRUE;
  1374.                   i = strlen( name ) - 1;
  1375.                   while (i >= 0) {
  1376.                      if (name[i] == ':' || name[i] == '\\')
  1377.                         break;
  1378.                      --i;
  1379.                   }
  1380.                   name[++i] = '\0';
  1381.  
  1382.                   assert( strlen( name ) + strlen( g_status.sas_dta.name )
  1383.                                    < MAX_COLS );
  1384.  
  1385.                   strcpy( g_status.sas_path, name );
  1386.                   strcpy( name, g_status.sas_path );
  1387.                   strcat( name, g_status.sas_dta.name );
  1388.                } else {
  1389.                   ++g_status.sas_arg;
  1390.                   if (win != NULL)
  1391.                      /*
  1392.                       * invalid path or file name
  1393.                       */
  1394.                      error( WARNING, win->bottom_line, win8 );
  1395.                }
  1396.             } else if (rc == ERROR)
  1397.                ++g_status.sas_arg;
  1398.          } else {
  1399.  
  1400.             /*
  1401.              * we already found one file with wild card characters,
  1402.              * find the next matching file.
  1403.              */
  1404.             rc = my_findnext( &g_status.sas_dta );
  1405.             if (rc == OK) {
  1406.  
  1407.                assert( strlen( g_status.sas_path ) +
  1408.                        strlen( g_status.sas_dta.name ) < MAX_COLS );
  1409.  
  1410.                strcpy( name, g_status.sas_path );
  1411.                strcat( name, g_status.sas_dta.name );
  1412.             } else {
  1413.                g_status.sas_found_first = FALSE;
  1414.                ++g_status.sas_arg;
  1415.             }
  1416.          }
  1417.  
  1418.          /*
  1419.           * if everything is everything so far, set up the file
  1420.           * and window structures and bring the file into the editor.
  1421.           */
  1422.          if (rc == OK) {
  1423.  
  1424.             assert( strlen( win19 ) + strlen( name ) < MAX_COLS );
  1425.  
  1426.             strcpy( searching, win19 );
  1427.             strcat( searching, name );
  1428.             save_screen_line( 0, bottom_line, line_buff );
  1429.             set_prompt( searching, bottom_line );
  1430.             file_mode = TEXT;
  1431.             bin_length = 0;
  1432.  
  1433.             assert( strlen( name ) <= MAX_COLS );
  1434.  
  1435.             _splitpath( name, spdrive, spdir, spname, spext );
  1436.             if (stricmp( spext, ".exe" ) == 0 || stricmp( spext, ".com" ) == 0){
  1437.                file_mode = BINARY;
  1438.                bin_length = g_status.file_chunk;
  1439.             }
  1440.             rc = attempt_edit_display( name, update_type, file_mode, bin_length );
  1441.             if (rc == OK)
  1442.                show_avail_mem( );
  1443.             restore_screen_line( 0, bottom_line, line_buff );
  1444.  
  1445.             if (rc == OK) {
  1446.                win = g_status.current_window;
  1447.                bin_offset_adjust( win, g_status.sas_rline );
  1448.                find_adjust( win, g_status.sas_ll, g_status.sas_rline,
  1449.                             g_status.sas_rcol );
  1450.                make_ruler( win );
  1451.                show_ruler( win );
  1452.                show_ruler_pointer( win );
  1453.                show_window_header( win );
  1454.                if (win->vertical)
  1455.                   show_vertical_separator( win );
  1456.                win->file_info->dirty = LOCAL;
  1457.             }
  1458.          }
  1459.  
  1460.          /*
  1461.           * either there are no more matching files or we had an
  1462.           * invalid file name, set rc to ERROR and let's look at the
  1463.           * next file name or pattern on the command line.
  1464.           */
  1465.          else
  1466.             rc = ERROR;
  1467.       }
  1468.    }
  1469.    if (rc == ERROR &&  g_status.sas_arg >= g_status.sas_argc  && win != NULL)
  1470.       /*
  1471.        * no more files to load
  1472.        */
  1473.       error( WARNING, win->bottom_line, win9 );
  1474.    return( rc );
  1475. }
  1476.  
  1477.  
  1478. /*
  1479.  * Name:    attempt_edit_display
  1480.  * Purpose: try to load then display a file
  1481.  * Date:    June 5, 1991
  1482.  * Passed:  fname:       file name to load
  1483.  *          update_type: update current window or entire screen
  1484.  *          file_mode:   BINARY or TEXT
  1485.  *          bin_len:     if opened in BINARY mode, length of binary lines
  1486.  * Notes:   When we first start the editor, we need to update the entire
  1487.  *          screen.  When we load in a new file, we only need to update
  1488.  *          the current window.
  1489.  */
  1490. int  attempt_edit_display( char *fname, int update_type, int file_mode,
  1491.                            int bin_len )
  1492. {
  1493. register int rc;
  1494. WINDOW *win;
  1495.  
  1496.    rc = edit_file( fname, file_mode, bin_len );
  1497.    if (rc != ERROR) {
  1498.       rc = initialize_window( );
  1499.       if (rc != ERROR) {
  1500.          win = g_status.current_window;
  1501.          if (update_type == LOCAL) {
  1502.             if (g_status.command != DefineGrep  &&
  1503.                         g_status.command != DefineRegXGrep  &&
  1504.                         g_status.command != RepeatGrep)
  1505.                redraw_current_window( win );
  1506.             show_file_count( g_status.file_count );
  1507.             show_window_count( g_status.window_count );
  1508.             show_avail_mem( );
  1509.          } else if (update_type == GLOBAL)
  1510.             redraw_screen( win );
  1511.          if (win->file_info->new_file) {
  1512.             g_status.command = AddLine;
  1513.             insert_newline( win );
  1514.             win->file_info->modified = FALSE;
  1515.          }
  1516.       }
  1517.    }
  1518.    return( rc );
  1519. }
  1520.  
  1521.  
  1522. /*
  1523.  * Name:    make_backup_fname
  1524.  * Purpose: add .bak to file name
  1525.  * Date:    January 6, 1992
  1526.  * Passed:  file: information allowing access to the current file
  1527.  */
  1528. void make_backup_fname( file_infos *file )
  1529. {
  1530. char name[MAX_COLS];            /* new name for file */
  1531. char *p;
  1532. int  i;
  1533. int  len;
  1534.  
  1535.    /*
  1536.     * if this is a new file then don't create a backup - can't backup
  1537.     *   a nonexisting file.
  1538.     */
  1539.    if (file->new_file)
  1540.       file->backed_up = TRUE;
  1541.  
  1542.    /*
  1543.     * find the file name extension if it exists
  1544.     */
  1545.    else {
  1546.       assert( strlen( file->file_name ) < MAX_COLS );
  1547.       strcpy( name, file->file_name );
  1548.       len = strlen( name );
  1549.       for (i=len,p=name+len; i>=0; i--) {
  1550.  
  1551.          /*
  1552.           * we found the '.' extension character.  get out
  1553.           */
  1554.          if (*p == '.')
  1555.             break;
  1556.  
  1557.          /*
  1558.           * we found the drive or directory character.  no extension so
  1559.           *  set the pointer to the end of file name string.
  1560.           */
  1561.          else if (*p == '\\' || *p == ':') {
  1562.             p = name + len;
  1563.             break;
  1564.  
  1565.          /*
  1566.           * we're at the beginning of the string - no '.', drive, or directory
  1567.           *  char was found.  set the pointer to the end of file name string.
  1568.           */
  1569.          } else if (i == 0) {
  1570.             p = name + len;
  1571.             break;
  1572.          }
  1573.          --p;
  1574.       }
  1575.       assert( strlen( name ) < MAX_COLS );
  1576.       strcpy( p, ".bak" );
  1577.       strcpy( file->backup_fname, name );
  1578.    }
  1579. }
  1580.  
  1581.  
  1582. /*
  1583.  * Name:    file_file
  1584.  * Purpose: To file the current file to disk.
  1585.  * Date:    September 17, 1991
  1586.  * Passed:  window:  pointer to current window
  1587.  */
  1588. int  file_file( WINDOW *window )
  1589. {
  1590.    if (save_file( window ) == OK)
  1591.       finish( window );
  1592.    return( OK );
  1593. }
  1594.  
  1595.  
  1596. /*
  1597.  * Name:    save_file
  1598.  * Purpose: To save the current file to disk.
  1599.  * Date:    June 5, 1991
  1600.  * Passed:  window:  pointer to current window
  1601.  * Notes:   If anything goes wrong, then the modified flag is set.
  1602.  *          If the file is saved successfully, then modified flag is
  1603.  *           cleared.
  1604.  */
  1605. int  save_file( WINDOW *window )
  1606. {
  1607. char name[MAX_COLS]; /* name of file to be saved */
  1608. register file_infos *file;
  1609. int  rc;
  1610. line_list_ptr temp_ll;
  1611.  
  1612.    entab_linebuff( );
  1613.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  1614.       return( ERROR );
  1615.    file = window->file_info;
  1616.    if (file->modified == FALSE)
  1617.       return( OK );
  1618.    /*
  1619.     * set up file name
  1620.     */
  1621.    assert( strlen( file->file_name ) < MAX_COLS );
  1622.    strcpy( name, file->file_name );
  1623.  
  1624.    /*
  1625.     * see if there was a file name - if not, then make the user
  1626.     *  supply one.
  1627.     */
  1628.    if (strlen( name ) == 0)
  1629.       rc = save_as_file( window );
  1630.    else {
  1631.       /*
  1632.        * save the file
  1633.        */
  1634.       rc = write_to_disk( window, name );
  1635.       if (rc != ERROR) {
  1636.          file->modified = FALSE;
  1637.          file->new_file = FALSE;
  1638.       }
  1639.    }
  1640.  
  1641.    /*
  1642.     * clear the dirty flags
  1643.     */
  1644.    if (rc == OK) {
  1645.       temp_ll = window->file_info->line_list;
  1646.       for (; temp_ll->len != EOF; temp_ll=temp_ll->next)
  1647.          temp_ll->dirty = FALSE;
  1648.       window->file_info->dirty = GLOBAL;
  1649.    }
  1650.    return( rc );
  1651. }
  1652.  
  1653.  
  1654. /*
  1655.  * Name:    save_backup
  1656.  * Purpose: To save a backup copy of the current file to disk.
  1657.  * Date:    June 5, 1991
  1658.  * Passed:  window:  pointer to current window
  1659.  */
  1660. int  save_backup( WINDOW *window )
  1661. {
  1662.    /*
  1663.     * set up file name
  1664.     */
  1665.    return( write_to_disk( window, window->file_info->backup_fname ) );
  1666. }
  1667.  
  1668.  
  1669. /*
  1670.  * Name:    write_to_disk
  1671.  * Purpose: To write file from memory to disk
  1672.  * Date:    June 5, 1991
  1673.  * Passed:  window:  pointer to current window
  1674.  *          fname:   file name to save on disk
  1675.  */
  1676. int  write_to_disk( WINDOW *window, char *fname )
  1677. {
  1678. char name[MAX_COLS]; /* name of file to be saved */
  1679. char status_line[MAX_COLS+2]; /* status line at top of window */
  1680. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1681. register file_infos *file;
  1682. int  rc;
  1683. int  prompt_line;
  1684. int  fattr;
  1685.  
  1686.    file = window->file_info;
  1687.    prompt_line = window->bottom_line;
  1688.  
  1689.    /*
  1690.     * set up file name
  1691.     */
  1692.    assert( strlen( fname ) < MAX_COLS );
  1693.    strcpy( name, fname );
  1694.    save_screen_line( 0, prompt_line, line_buff );
  1695.    eol_clear( 0, prompt_line, g_display.message_color );
  1696.  
  1697.    /*
  1698.     * saving
  1699.     */
  1700.    combine_strings( status_line, utils6, name, "'" );
  1701.    s_output( status_line, prompt_line, 0, g_display.message_color );
  1702.    if ((rc = hw_save( name, file, 1L, file->length, NOTMARKED )) == ERROR) {
  1703.       if (ceh.flag != ERROR) {
  1704.          if (get_fattr( name, &fattr ) == OK && fattr & READ_ONLY)
  1705.             /*
  1706.              * file is read only
  1707.              */
  1708.             combine_strings( status_line, utils7a, name, utils7b );
  1709.          else
  1710.             /*
  1711.              * cannot write to
  1712.              */
  1713.             combine_strings( status_line, utils8, name, "'" );
  1714.          error( WARNING, prompt_line, status_line );
  1715.       }
  1716.    }
  1717.    restore_screen_line( 0, prompt_line, line_buff );
  1718.    return( rc );
  1719. }
  1720.  
  1721.  
  1722. /*
  1723.  * Name:    save_as_file
  1724.  * Purpose: To save the current file to disk, but under a new name.
  1725.  * Date:    June 5, 1991
  1726.  * Passed:  window:  pointer to current window
  1727.  */
  1728. int  save_as_file( WINDOW *window )
  1729. {
  1730. char name[MAX_COLS];            /* new name for file */
  1731. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1732. int  prompt_line;
  1733. int  rc;
  1734. int  fattr;
  1735. register WINDOW *win;           /* put window pointer in a register */
  1736. line_list_ptr temp_ll;
  1737.  
  1738.    win = window;
  1739.    entab_linebuff( );
  1740.    if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1741.       return( ERROR );
  1742.    /*
  1743.     * read in name
  1744.     */
  1745.    prompt_line = win->bottom_line;
  1746.    save_screen_line( 0, prompt_line, line_buff );
  1747.    name[0] = '\0';
  1748.    /*
  1749.     * new file name:
  1750.     */
  1751.    if ((rc = get_name( utils9, prompt_line, name,
  1752.                        g_display.message_color )) == OK  &&  *name != '\0') {
  1753.  
  1754.        /*
  1755.         * make sure it is OK to overwrite any existing file
  1756.         */
  1757.       rc = get_fattr( name, &fattr );
  1758.       if (rc == OK) {   /* file exists */
  1759.          /*
  1760.           * overwrite existing file?
  1761.           */
  1762.          set_prompt( utils10, prompt_line );
  1763.          if (get_yn( ) != A_YES  ||  change_mode( name, prompt_line ) == ERROR)
  1764.             rc = ERROR;
  1765.       }
  1766.       if (rc != ERROR)
  1767.          rc = write_to_disk( win, name );
  1768.  
  1769.       /*
  1770.        * depending on personal taste, you may want to uncomment the next
  1771.        *  lines to clear the dirty flags.
  1772.        */
  1773. /*
  1774.  *     if (rc == OK) {
  1775.  *        temp_ll = window->file_info->line_list;
  1776.  *        for (; temp_ll->len != EOF; temp_ll=temp_ll->next)
  1777.  *           temp_ll->dirty = FALSE;
  1778.  *        window->file_info->dirty = GLOBAL;
  1779.  *     }
  1780.  */
  1781.    }
  1782.    restore_screen_line( 0, prompt_line, line_buff );
  1783.    return( rc );
  1784. }
  1785.  
  1786.  
  1787. /*
  1788.  * Name:    change_fattr
  1789.  * Purpose: To change the file attributes
  1790.  * Date:    December 31, 1991
  1791.  * Passed:  window:  pointer to current window
  1792.  */
  1793. int  change_fattr( WINDOW *window )
  1794. {
  1795. char name[MAX_COLS];            /* new name for file */
  1796. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1797. file_infos *file;
  1798. WINDOW *wp;
  1799. int  prompt_line;
  1800. register int ok;
  1801. unsigned char fattr;
  1802. char *s;
  1803. int  rc;
  1804.  
  1805.    prompt_line = window->bottom_line;
  1806.    save_screen_line( 0, prompt_line, line_buff );
  1807.    name[0] = '\0';
  1808.    /*
  1809.     * enter new file attributes
  1810.     */
  1811.    if ((ok = get_name( utils14, prompt_line, name,
  1812.                        g_display.message_color )) == OK) {
  1813.       if (*name != '\0') {
  1814.          fattr = 0;
  1815.          s = name;
  1816.  
  1817.          /*
  1818.           * yes, I know lint complains about "ok = *s++".
  1819.           */
  1820.          while (ok = *s++) {
  1821.             switch (ok) {
  1822.                case 'a' :
  1823.                case 'A' :
  1824.                   fattr |= ARCHIVE;
  1825.                   break;
  1826.                case 's' :
  1827.                case 'S' :
  1828.                   fattr |= SYSTEM;
  1829.                   break;
  1830.                case 'h' :
  1831.                case 'H' :
  1832.                   fattr |= HIDDEN;
  1833.                   break;
  1834.                case 'r' :
  1835.                case 'R' :
  1836.                   fattr |= READ_ONLY;
  1837.                   break;
  1838.                default :
  1839.                   break;
  1840.             }
  1841.          }
  1842.          file = window->file_info;
  1843.          if (set_fattr( file->file_name, fattr ))
  1844.             /*
  1845.              * new file attributes not set
  1846.              */
  1847.             error( WARNING, prompt_line, utils15 );
  1848.          else {
  1849.             file->file_attrib = fattr;
  1850.             for (wp=g_status.window_list; wp!=NULL; wp=wp->next) {
  1851.                if (wp->file_info == file && wp->visible)
  1852.                   show_window_fname( wp );
  1853.             }
  1854.          }
  1855.       }
  1856.       rc = OK;
  1857.    } else
  1858.       rc = ERROR;
  1859.    restore_screen_line( 0, prompt_line, line_buff );
  1860.    return( rc );
  1861. }
  1862.  
  1863.  
  1864. /*
  1865.  * Name:    get_fattr
  1866.  * Purpose: To get dos file attributes
  1867.  * Date:    December 26, 1991
  1868.  * Passed:  fname: ASCIIZ file name.  Null terminated file name
  1869.  *          fattr: pointer to file attributes
  1870.  * Returns: 0 if successfull, non zero if not
  1871.  * Notes:   Uses the DOS function to get file attributes.  I really didn't
  1872.  *           like the file attribute functions in the C library:  fstat() and
  1873.  *           stat() or access() and chmod().
  1874.  *           FYI, File Attributes:
  1875.  *              0x00 = Normal.  Can be read or written w/o restriction
  1876.  *              0x01 = Read-only.  Cannot be opened for write; a file with
  1877.  *                     the same name cannot be created.
  1878.  *              0x02 = Hidden.  Not found by directory search.
  1879.  *              0x04 = System.  Not found by directory search.
  1880.  *              0x08 = Volumn Label.
  1881.  *              0x10 = Directory.
  1882.  *              0x20 = Archive.  Set whenever the file is changed, or
  1883.  *                     cleared by the Backup command.
  1884.  *           Return codes:
  1885.  *              0 = No error
  1886.  *              1 = AL not 0 or 1
  1887.  *              2 = file is invalid or does not exist
  1888.  *              3 = path is invalid or does not exist
  1889.  *              5 = Access denied
  1890.  */
  1891. int  get_fattr( char far *fname, int *fattr )
  1892. {
  1893. int  rc;                /* return code */
  1894. int  attr;
  1895.  
  1896.    ASSEMBLE {
  1897.         push    ds
  1898.         mov     dx, WORD PTR fname      /* get OFFSET of filename string */
  1899.         mov     ax, WORD PTR fname+2    /* get SEGMENT of filename string */
  1900.         mov     ds, ax                  /* put SEGMENT in ds */
  1901.         mov     ax, 0x4300              /* function:  get file attributes */
  1902.         int     0x21                    /* DOS interrupt */
  1903.         pop     ds
  1904.  
  1905.         jc      an_error                /* save the error code from get attr */
  1906.         xor     ax, ax                  /* if no carry, no error */
  1907.         jmp     SHORT get_out           /* lets get out */
  1908.    }
  1909. an_error:
  1910.  
  1911.  
  1912.    ASSEMBLE {
  1913.         xor     cx, cx                  /* if error, then zero out cx - attrs */
  1914.    }
  1915. get_out:
  1916.  
  1917.    ASSEMBLE {
  1918.         mov     WORD PTR rc, ax         /* ax contains error number on error */
  1919.         mov     WORD PTR attr, cx       /* cx contains file attributes */
  1920.    }
  1921.    *fattr = attr;
  1922.    if (ceh.flag == ERROR)
  1923.       rc = ERROR;
  1924.    return( rc );
  1925. }
  1926.  
  1927.  
  1928. /*
  1929.  * Name:    set_fattr
  1930.  * Purpose: To set dos file attributes
  1931.  * Date:    December 26, 1991
  1932.  * Passed:  fname: ASCIIZ file name.  Null terminated file name
  1933.  *          fattr: file attributes
  1934.  * Returns: 0 if successfull, non zero if not
  1935.  * Notes:   Uses the DOS function to get file attributes.
  1936.  *           Return codes:
  1937.  *              0 = No error
  1938.  *              1 = AL not 0 or 1
  1939.  *              2 = file is invalid or does not exist
  1940.  *              3 = path is invalid or does not exist
  1941.  *              5 = Access denied
  1942.  */
  1943. int  set_fattr( char far *fname, int fattr )
  1944. {
  1945. int  rc;                /* return code */
  1946.  
  1947.    ASSEMBLE {
  1948.         push    ds
  1949.         mov     dx, WORD PTR fname      /* get OFFSET of filename string */
  1950.         mov     ax, WORD PTR fname+2    /* get SEGMENT of filename string */
  1951.         mov     ds, ax                  /* put SEGMENT in ds */
  1952.         mov     cx, WORD PTR fattr      /* cx contains file attributes */
  1953.         mov     ax, 0x4301              /* function:  get file attributes */
  1954.         int     0x21                    /* DOS interrupt */
  1955.         pop     ds
  1956.  
  1957.         jc      get_out                 /* save the error code from get attr */
  1958.         xor     ax, ax                  /* if no carry, no error */
  1959.    }
  1960. get_out:
  1961.  
  1962.    ASSEMBLE {
  1963.         mov     WORD PTR rc, ax         /* ax contains error number on error */
  1964.    }
  1965.    if (ceh.flag == ERROR)
  1966.       rc = ERROR;
  1967.    return( rc );
  1968. }
  1969.  
  1970.  
  1971. /*
  1972.  * Name:    get_current_directory
  1973.  * Purpose: get current directory
  1974.  * Date:    February 13, 1992
  1975.  * Passed:  path:  pointer to buffer to store path
  1976.  *          drive: drive to get current directory
  1977.  * Notes:   use simple DOS interrupt
  1978.  */
  1979. int  get_current_directory( char far *path, int drive )
  1980. {
  1981. int  rc;
  1982.  
  1983.    ASSEMBLE {
  1984.         push    si                      /* save register vars if any */
  1985.         push    ds                      /* save ds */
  1986.  
  1987.         mov     dx, WORD PTR drive      /* dl = drive, 0 = default, 1 = a, etc.. */
  1988.         mov     si, WORD PTR path       /* get OFFSET of path */
  1989.         mov     ax, WORD PTR path+2     /* get SEGMENT of path */
  1990.         mov     ds, ax                  /* put it in ds */
  1991.         mov     ah, 0x47                /* function 0x47 == get current dir */
  1992.         int     0x21                    /* standard DOS interrupt */
  1993.         xor     ax, ax                  /* zero out ax, return OK if no error */
  1994.         jnc     no_error                /* if carry set, then an error */
  1995.         mov     ax, ERROR               /* return -1 if error */
  1996.    }
  1997. no_error:
  1998.  
  1999.    ASSEMBLE {
  2000.         pop     ds                      /* get back ds */
  2001.         pop     si                      /* get back si */
  2002.         mov     WORD PTR rc, ax         /* save return code */
  2003.    }
  2004.    if (ceh.flag == ERROR)
  2005.       rc = ERROR;
  2006.    return( rc );
  2007. }
  2008.  
  2009.  
  2010. /*
  2011.  * Name:    set_current_directory
  2012.  * Purpose: set current directory
  2013.  * Date:    February 13, 1992
  2014.  * Passed:  new_path: directory path, which may include drive letter
  2015.  * Notes:   use simple DOS interrupt
  2016.  */
  2017. int  set_current_directory( char far *new_path )
  2018. {
  2019. int  rc;
  2020.  
  2021.    ASSEMBLE {
  2022.         push    ds                      /* save ds */
  2023.  
  2024.         mov     dx, WORD PTR new_path   /* get OFFSET of new_path */
  2025.         mov     ax, WORD PTR new_path+2 /* get SEGMENT of new_path */
  2026.         mov     ds, ax                  /* put it in ds */
  2027.         mov     ah, 0x3b                /* function 0x3b == set current dir */
  2028.         int     0x21                    /* standard DOS interrupt */
  2029.         xor     ax, ax                  /* zero out ax, return OK if no error */
  2030.         jnc     no_error                /* if carry set, then an error */
  2031.         mov     ax, ERROR               /* return -1 if error */
  2032.    }
  2033. no_error:
  2034.  
  2035.    ASSEMBLE {
  2036.         pop     ds                      /* get back ds */
  2037.         mov     WORD PTR rc, ax         /* save return code */
  2038.    }
  2039.    if (ceh.flag == ERROR)
  2040.       rc = ERROR;
  2041.    return( rc );
  2042. }
  2043.